/**
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY. 
 * See the GNU General Public License for more details.
 * 
 * Copyright (C) 2011 CellObject.net
 */
package net.cellobject.javacodeformatter.view;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IMemento;

/**
 * A viewer sorter is used by a structured viewer to reorder the elements provided by its content provider. In our case, the
 * <code>TPFormatterViewSorter</code> delegates sorting to the comparators. In addition, the TPFormatterViewSorter listens for
 * mouse clicks in the column headers and resorts the table content based on the column that was selected. Clicking on a column a
 * second time toggles the sort order.
 */
public class FormatterViewSorter extends ViewerSorter {
	private static final String TAG_DESCENDING = "descending";

	private static final String TAG_COLUMN_INDEX = "columnIndex";

	private static final String TAG_TYPE = "SortInfo";

	private static final String TAG_TRUE = "true";

	// Simple data structure for grouping
	// sort information by column.
	private class SortInfo {
		int columnIndex;

		Comparator<Object> comparator;

		boolean descending;
	}

	private final TableViewer viewer;

	private SortInfo[] infos;

	public FormatterViewSorter(final TableViewer viewer, final TableColumn[] columns, final Comparator<Object>[] comparators) {
		this.viewer = viewer;
		infos = new SortInfo[columns.length];
		for (int i = 0; i < columns.length; i++) {
			infos[i] = new SortInfo();
			infos[i].columnIndex = i;
			infos[i].comparator = comparators[i];
			infos[i].descending = false;
			createSelectionListener(columns[i], infos[i]);
		}
	}

	@Override
	public int compare(final Viewer viewer, final Object favorite1, final Object favorite2) {
		for (final SortInfo info : infos) {
			final int result = info.comparator.compare(favorite1, favorite2);
			if (result != 0) {
				if (info.descending) {
					return -result;
				}
				return result;
			}
		}
		return 0;
	}

	private void createSelectionListener(final TableColumn column, final SortInfo info) {
		column.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(final SelectionEvent e) {
				sortUsing(info);
			}
		});
	}

	protected void sortUsing(final SortInfo info) {
		if (info == infos[0]) {
			info.descending = !info.descending;
		} else {
			for (int i = 0; i < infos.length; i++) {
				if (info == infos[i]) {
					System.arraycopy(infos, 0, infos, 1, i);
					infos[0] = info;
					info.descending = false;
					break;
				}
			}
		}
		viewer.refresh();
	}

	public void saveState(final IMemento memento) {
		for (final SortInfo info : infos) {
			final IMemento mem = memento.createChild(TAG_TYPE);
			mem.putInteger(TAG_COLUMN_INDEX, info.columnIndex);
			if (info.descending) {
				mem.putString(TAG_DESCENDING, TAG_TRUE);
			}
		}
	}

	public void init(final IMemento memento) {
		final List<SortInfo> newInfos = new ArrayList<SortInfo>(infos.length);
		final IMemento[] mems = memento.getChildren(TAG_TYPE);
		for (final IMemento mem : mems) {
			final Integer value = mem.getInteger(TAG_COLUMN_INDEX);
			if (value == null) {
				continue;
			}
			final int index = value.intValue();
			if ( (index < 0) || (index >= infos.length)) {
				continue;
			}
			final SortInfo info = infos[index];
			if (newInfos.contains(info)) {
				continue;
			}
			info.descending = TAG_TRUE.equals(mem.getString(TAG_DESCENDING));
			newInfos.add(info);
		}
		for (int i = 0; i < infos.length; i++) {
			if (!newInfos.contains(infos[i])) {
				newInfos.add(infos[i]);
			}
		}
		infos = newInfos.toArray(new SortInfo[newInfos.size()]);
	}
}
